/*
	The MIT License

	Copyright (c) 2008 IFMO/GameDev Studio

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.
*/
// com_main.cpp - common initialization, shutting down and processing


#include "..\global.h"
#include "core.h"


const char *ECore::Version(void)
{
	const char *version_string = 
#ifdef EDITOR
		"Eclipse editor "
#else	
		"CUDA Test "
#endif
		"0.1 "
#ifdef _DEBUG
		"debug "
#else
		"release "
#endif
		__DATE__
		;

	return version_string;	
}



//
//	ECore::ECore
//
ECore::ECore(void)
{
	ap.core		=	this;
	ap.con		=	con		=	CreateConsole();
	LOGF("%s\r\n", Version());

	ap.sys		=	sys		=	CreateSystem(con);
	ap.fs		=	fs		=	CreateFileSystem(con);
	ap.shell	=	shell	=	CreateShell(con, fs);

	EArgs	args(sys->GetCmdLine());


	if (args.CheckParam("-cfg")) {
		int i = args.CheckParam("-cfg");
		cfg_name	=	args.Argv(i+1);
	} else {	
		cfg_name	=	"config.cfg";
	}


	shell->AddCommand("quit",		(EShellCmd_f)Quit_f,			this);
	shell->AddCommand("infoTiming",	(EShellCmd_f)InfoTiming_f,	this);
	shell->AddCommand("fatal",		(EShellCmd_f)Fatal_f,		this);
	shell->AddCommand("profDump",	(EShellCmd_f)ProfDump_f,		this);
	shell->AddCommand("profReset",	(EShellCmd_f)ProfReset_f,	this);


	LOG_SPLIT2("Common initialization");

	gs.is	=	is	=	CreateInputSystem(ap);	

	shell->ExecuteConfig("default.cfg");
	shell->ExecuteEnqueuedCommands();

	shell->ExecuteConfig(cfg_name.c_str());
	shell->ExecuteEnqueuedCommands();

	fs->ChDir("data");
	fs->UpdatePackInfo();

	gs.rs	=	rs		=	CreateRenderSystem(ap);

	gs2.ui	=	ui		=	CreateUserInterface(ap, gs);
	

	con->DropWarnings();
	LOG_SPLIT2("");


	core_show_fps   = shell->RegisterVar("core_show_fps",   "0",    0);
	core_min_dtime  = shell->RegisterVar("core_min_dtime",  "0.01", 0);
	core_max_dtime  = shell->RegisterVar("core_max_dtime",  "0.10", 0);
	core_time_scale = shell->RegisterVar("core_time_scale", "1",    0);

	frame = 0;
	old_time = time = sys->Milliseconds();
}


//
//	ECore::~ECore
//
ECore::~ECore(void)
{
	LOG_SPLIT2("Common shutting down");
	
	DestroyUserInterface(ui);

	DestroyRenderSystem(rs);
	

	fs->ChDir(NULL);
	shell->WriteConfig(cfg_name.c_str());
	is->AppendKeyBindings(cfg_name.c_str());

	DestroyInputSystem(is);

	LOG_SPLIT2("");

	DestroyShell(shell);
	DestroyFileSystem(fs);
	DestroySystem(sys);
	DestroyConsole(con);
}


//
//	ECore::Frame
//
void ECore::Frame(void)
{
	PROF_PROBE();
	
	UpdateTimer();

		rs->BeginFrame();
		
		mouseInput_s mi;

		bool allow_kb = true;
		if (ui->GetActiveFrame()) {
			allow_kb = false;
		};
		
		is->ProcessInput(rs->GetWndDescriptor(), allow_kb, false, false, true, mi);

		
		rs->RenderFrame();
		ui->MouseMove(&mi);
		
		ShowDebugStrings();
		
	rs->EndFrame();

	ProcessShellCommands();
	con->DropWarnings();
}


//
//	ECore::ProcessShellCommands
//
void ECore::ProcessShellCommands(void)
{
	char buffer[512];
	
	//	Read command from console :
	if (con->GetCmdLine(buffer, 511)) {

		//	This is a normal shell command :
		if (buffer[0]!='*') {
			shell->ExecuteString(va("%s;", buffer));
			LOGF("]%s\r\n", buffer);

			//	Push commmand to 'history' list :
			latest_command.push_back(buffer);
			latest_command_top = (int)latest_command.size();
		}

		//	This is a 'helper'-command begining with '*' :
		if (buffer[0]=='*') {
			if (strcmp(buffer, "*complete")==0) {
				// get next command in queue :
				while (!con->GetCmdLine(buffer, 511)) {
				}
				
				con->SetCmdLine(shell->CompleteCommand(buffer));
			}

			//	Get previous command :
			if (strcmp(buffer, "*prev")==0) {
				latest_command_top--;
				latest_command_top = Clamp<int>(latest_command_top, 0, (int)latest_command.size()-1);

				if (!latest_command.empty()) {
					con->SetCmdLine( latest_command[ latest_command_top ].c_str() );
				} else {
					con->SetCmdLine("");
				}
			}
			
			//	Get next command :
			if (strcmp(buffer, "*next")==0) {
				latest_command_top++;
				latest_command_top = Clamp<int>(latest_command_top, 0, (int)latest_command.size()-1);

				if (!latest_command.empty()) {
					con->SetCmdLine( latest_command[ latest_command_top ].c_str() );
				} else {
					con->SetCmdLine("");
				}
			}
		}
	}
	
	shell->ExecuteEnqueuedCommands();
}


//
//	ECore::AddDebugString
//
void ECore::AddDebugString(const char *str)
{
	dbg_strings.push_back(str);
}


//
//	ECore::ShowDebugStrings
//
void ECore::ShowDebugStrings(void)
{
	#define FONT_NAME	"chars_courier.tga"
	#define FONT_TRACK  8
	#define FONT_HEIGHT	13
	#define FONT_SIZE	16

	uint w, h;
	rs->GetScreenSize(w, h);
	rs->Set2DViewMode();

	if (core_show_fps->Int() || true) {	   // TODO : restore var checking!
		char fps_str[64];
		sprintf(fps_str, "fps %5.2f", 1.0/GetDeltaTime());
		ui->DrawStringMT(w-FONT_TRACK*strlen(fps_str)-1, FONT_HEIGHT+1, FONT_SIZE, FONT_TRACK, FONT_NAME, fps_str, EVec4(1,1,1,1));
	}

	//
	//	Show strings :	
	//
	if (dbg_strings.size()!=0) {
		float x = 32;
		float y = 32;
		
		uint max_length = 0;
		for (uint i=0; i<dbg_strings.size(); i++) {
			max_length = max(max_length, dbg_strings[i].length());
		}
		
		rs->DrawStretchImage(x-4, y-4, max_length * FONT_TRACK+8, dbg_strings.size() * FONT_HEIGHT+8, "*black", EVec4(0,0,0, 0.5));
		
		for (uint i=0; i<dbg_strings.size(); i++) {
			ui->DrawStringMT(x+1, y + (i+1)*FONT_HEIGHT+1, FONT_SIZE, FONT_TRACK, FONT_NAME, dbg_strings[i].c_str(), EVec4(0,0,0,1));
			ui->DrawStringMT(x,   y + (i+1)*FONT_HEIGHT,   FONT_SIZE, FONT_TRACK, FONT_NAME, dbg_strings[i].c_str(), EVec4(1,1,1,1));
		}
		
		dbg_strings.clear();
	}	
}


//
//	ECore::GetDeltaTime
//
float ECore::GetDeltaTime(void)
{
	float ftime  = dtime * 0.001;
	float min_dt = core_min_dtime->Float();
	float max_dt = core_max_dtime->Float();
	ftime = Clamp<float>(ftime, min_dt, max_dt) * core_time_scale->Float();
	return ftime;
}


//
//	ECore::UpdateTimer
//
void ECore::UpdateTimer(void)
{
	//sys->Sleep(4);
	do {
		time = sys->Milliseconds();
		dtime = time - old_time;
	} while( !dtime );
	
	old_time = time;
	frame++;
}


/*-----------------------------------------------------------------------------
	Console stuff :
-----------------------------------------------------------------------------*/

void ECore::ProfDump_f(ECore *self, int argc, char **argv)
{
	profiler.DumpStats(self->con, false);
}


void ECore::ProfReset_f(ECore *self, int argc, char **argv)
{
	profiler.ResetProfiler();
}


void ECore::Fatal_f(ECore *self, int argc, char **argv)
{
	FATAL("Test Fatal Error");
}

void ECore::Quit_f(ECore *self, int argc, char **argv)
{
	self->sys->Quit(0);
}


void ECore::InfoTiming_f(ECore *self, int argc, char **argv)
{
	IConsole	*con = self->con;
	LOGF("Timing : delta=%4d  frame=%4d\r\n", self->dtime, self->frame);
}


/*-----------------------------------------------------------------------------
	UI stuff :
-----------------------------------------------------------------------------*/

void ECore::KeyDown(uint code) 
{
	ui->KeyDown(code);
}


void ECore::KeyPress(uint code)
{
	ui->KeyPress(code);
}


void ECore::MouseMove(int x, int y)
{
	//ui->MouseMove(x, y);
}


void ECore::UpdateLoadingScreen(uint load_object_num)
{
}


